#include "BlockQueue.hpp"
#include "Task.hpp"
#include <ctime>
#include <unistd.h>

// void* produce(void* args)
// {
//     BlockQueue<int>* bq = static_cast<BlockQueue<int>*>(args);
//     while(true)
//     {
//         // sleep(1);
//         // 生产行为
//         int data = rand() % 10 + 1;
//         bq->push(data);
//         std::cout << "生产数据：" << data << std::endl;
//     }

//     return nullptr;
// }

// void* consume(void* args)
// {
//     BlockQueue<int>* bq = static_cast<BlockQueue<int>*>(args);
//     while(true)
//     {
//         sleep(1);
//         // 消费行为
//         int data = 0;
//         bq->pop(&data);
//         std::cout << "消费数据：" << data << std::endl;
//     }

//     return nullptr;
// }

// int main()
// {
//     srand((unsigned long)time(nullptr) ^ getpid()) ;

//     BlockQueue<int>* bq = new BlockQueue<int>();// 共享资源
//     pthread_t producer, consumer;
//     pthread_create(&producer, nullptr, produce, bq);
//     pthread_create(&consumer, nullptr, consume, bq);

//     pthread_join(producer, nullptr);
//     pthread_join(consumer, nullptr);

//     return 0;
// }


// const std::string op = "+-*/%";
// int myMath(int x, int y, char op)
// {
//     int result = 0;
//     switch (op)
//     {
//     case '+':
//         result = x + y;
//         break;
//     case '-':
//         result = x - y;
//         break;
//     case '*':
//         result = x * y;
//         break;
//     case '/':
//         if(y == 0)
//         {
//             std::cerr << "division 0 error" << std::endl;
//             result = -1;
//         }  
//         else
//             result = x / y;
//         break;
//     case '%':
//         if(y == 0)
//         {
//             std::cerr << "mod 0 error" << std::endl;
//             result = -1;
//         }
//         else
//             result = x % y;
//         break;
//     default:
//         break;
//     }

//     return result;
// }

// void *produce(void *args)
// {
//     BlockQueue<Task> *bq = static_cast<BlockQueue<Task> *>(args);
//     while (true)
//     {
//         // sleep(1);
//         // 生产行为
//         int x = rand() % 100;
//         int y = rand() % 10;
//         int opi = rand() % 5;

//         Task t(x, y, op[opi], myMath);
        
//         bq->push(t);
//         std::cout << "生产任务：" << t.toTaskString() << std::endl;
//     }

//     return nullptr;
// }

// void *consume(void *args)
// {
//     BlockQueue<Task> *bq = static_cast<BlockQueue<Task> *>(args);
//     while (true)
//     {
//         sleep(1);
//         // 消费行为
//         Task t;
//         bq->pop(&t);
//         std::cout << "消费任务：" << t() << std::endl << std::endl;
//     }

//     return nullptr;
// }

// int main()
// {
//     srand((unsigned long)time(nullptr) ^ getpid());

//     BlockQueue<Task> *bq = new BlockQueue<Task>(); // 共享资源
//     pthread_t producer, consumer;
//     pthread_create(&producer, nullptr, produce, bq);// 生产者
//     pthread_create(&consumer, nullptr, consume, bq);// 消费者

//     pthread_join(producer, nullptr);
//     pthread_join(consumer, nullptr);

//     return 0;
// }




// C：计算  S：存储
template<class C, class S>
class BlockQueues
{
public:
    BlockQueues():_c_bq(nullptr), _s_bq(nullptr)
    {}
    ~BlockQueues()
    {
        if(_c_bq) delete _c_bq;
        if(_s_bq) delete _s_bq;
    }
public:
    // 都是阻塞队列
    BlockQueue<C>* _c_bq;// 计算队列
    BlockQueue<S>* _s_bq;// 存储队列
};

void *produce(void *args)
{
    BlockQueue<CalculateTask> *bq = static_cast<BlockQueues<CalculateTask, SaveTask> *>(args)->_c_bq;
    while (true)
    {
        // sleep(5);
        // 生产行为——生产 计算任务
        int x = rand() % 100;
        int y = rand() % 10;
        int opi = rand() % 5;
        // 这里的x，y，opi可能是从网络、外设等获取的

        CalculateTask t(x, y, op[opi], myMath);
        
        // 将计算任务添加到计算阻塞队列中
        bq->push(t);

        std::cout << "producer 生产任务：" << t.toTaskString() << std::endl;

        sleep(1);
    }

    return nullptr;
}

void *consume(void *args)
{
    BlockQueue<CalculateTask> *bq = static_cast<BlockQueues<CalculateTask, SaveTask> *>(args)->_c_bq;
    BlockQueue<SaveTask>* save_bq =static_cast<BlockQueues<CalculateTask, SaveTask>*>(args)->_s_bq;

    while (true)
    {
        // 消费行为

        // 从计算阻塞队列中读取任务
        CalculateTask t;
        bq->pop(&t);

        // 执行从阻塞队列中读取到的任务，并记录执行结果
        const std::string result = t();
        const std::string task = t.toTaskString();
        std::cout << "consumer 计算任务：" << result << " ...done" << std::endl;

        // 将计算任务的执行结果构建成存储任务推送到存储阻塞队列中
        SaveTask save_t(result, task);
        save_bq->push(save_t);
        std::cout << "consumer 推送存储任务 完成" << std::endl;
        // sleep(1);
    }

    return nullptr;
}

void* save(void* args)
{
    BlockQueue<SaveTask>* save_bq =static_cast<BlockQueues<CalculateTask, SaveTask>*>(args)->_s_bq;
    while(true)
    {
        // 从存储阻塞队列中或缺任务
        SaveTask save_t;
        save_bq->pop(&save_t);
        // 执行任务
        save_t();

        std::cout << "saver 存储任务:" << save_t.get_task_name() << " 完成" << std::endl << std::endl;
    }

    return nullptr; 
}

int main()
{
    srand((unsigned long)time(nullptr) ^ getpid());

    BlockQueues<CalculateTask, SaveTask> bqs;// 阻塞队列集

    bqs._c_bq = new BlockQueue<CalculateTask>(); // 计算队列
    bqs._s_bq = new BlockQueue<SaveTask>();// 存储队列，存储计算任务的结果

    // pthread_t producer, consumer, saver;
    // pthread_create(&producer, nullptr, produce, &bqs);// 生产者
    // pthread_create(&consumer, nullptr, consume, &bqs);// 消费者
    // pthread_create(&saver, nullptr, save, &bqs);// 保存

    // pthread_join(producer, nullptr);
    // pthread_join(consumer, nullptr);

    // 多个生产者和消费者也可以，例如当多个生产者同时要进入临界区，他们就要争夺锁
    pthread_t producer[3], consumer[2], saver;
    pthread_create(producer, nullptr, produce, &bqs);// 生产者
    pthread_create(producer + 1, nullptr, produce, &bqs);// 生产者
    pthread_create(producer + 2, nullptr, produce, &bqs);// 生产者
    pthread_create(consumer, nullptr, consume, &bqs);// 消费者
    pthread_create(consumer + 1, nullptr, consume, &bqs);// 消费者
    pthread_create(&saver, nullptr, save, &bqs);// 保存

    pthread_join(producer[0], nullptr);
    pthread_join(producer[1], nullptr);
    pthread_join(producer[2], nullptr);
    pthread_join(consumer[0], nullptr);
    pthread_join(consumer[1], nullptr);
    pthread_join(saver, nullptr);

    return 0;
}